﻿using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows.Forms;
using ZXing;
using ZXing.Common;
using ZXing.Multi.QrCode;



namespace BarcodeScan
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //https://demo.dynamsoft.com/DBR/BarcodeReaderDemo.aspx
        private void Button1_Click(object sender, EventArgs e)
        {
            string fileurl = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "选择需要扫描的图片";
            //fdlg.InitialDirectory = @"c:\";   //@是取消转义字符的意思
            fdlg.Filter = "All files（*.*）|*.*|All files(*.*)|*.* ";
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                fileurl = fdlg.FileName;

                using (FileStream fs = new FileStream(fileurl, FileMode.Open))
                {
                    //QRCodeMultiReader qc = new QRCodeMultiReader();
                    Image image = Image.FromStream(fs);
                    Bitmap bitmap = new Bitmap(image);

                    ZXing.MultiFormatReader multiFormatReader = new ZXing.MultiFormatReader();
                    ZXing.Multi.GenericMultipleBarcodeReader multiBarcodeReader = new ZXing.Multi.GenericMultipleBarcodeReader(multiFormatReader);
                    //LuminanceSource source = new BitmapLuminanceSource(bitmap);
                    LuminanceSource source = new BitmapLuminanceSource(bitmap);

                    ZXing.BinaryBitmap bBitmap = new ZXing.BinaryBitmap(new HybridBinarizer(source));
                    ZXing.Result[] zResults = multiBarcodeReader.decodeMultiple(bBitmap);

                    //LuminanceSource source = new BitmapLuminanceSource(bitmap);
                    //BinaryBitmap binarybitmap = new BinaryBitmap(new HybridBinarizer(source));
                    //IDictionary<DecodeHintType, object> hints = new Dictionary<DecodeHintType, object>();
                    //hints.Add(DecodeHintType.CHARACTER_SET, "UTF-8");
                    //hints.Add(DecodeHintType.TRY_HARDER, "3");
                    //Result[] r = qc.decodeMultiple(binarybitmap, hints);
                    foreach (Result res in zResults)
                    {
                        textBox1.Text += res.Text + "\r\n";
                    }
                }


            }



        }

        public static byte[] ObjectToBytes(object obj)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                return ms.GetBuffer();
            }
        }

        private void Button2_Click(object sender, EventArgs e)
        {
            string fileurl = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "选择需要扫描的图片";
            //fdlg.InitialDirectory = @"c:\";   //@是取消转义字符的意思
            fdlg.Filter = "All files（*.*）|*.*|All files(*.*)|*.* ";
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                fileurl = fdlg.FileName;

                using (FileStream fs = new FileStream(fileurl, FileMode.Open))
                {
                    //QRCodeMultiReader qc = new QRCodeMultiReader();
                    Image image = Image.FromStream(fs);
                    Bitmap bitmap = (Bitmap)image;
                    BarcodeReader reader = new BarcodeReader();
                    //LuminanceSource ls = new BitmapLuminanceSource(bitmap);
                    //BinaryBitmap bb = new BinaryBitmap(new HybridBinarizer(ls));
                    Bitmap pImg = MakeGrayscale3((Bitmap)bitmap);

                    picCode.Image = pImg;

                    try
                    {
                        reader.Options.CharacterSet = "UTF-8";
                        Result result = reader.Decode(bitmap);
                        textBox1.Text = result.Text;
                    }
                    catch
                    {
                        MessageBox.Show("没有识别出条码");
                    }
                }
            }
        }

        private void Button3_Click(object sender, EventArgs e)
        {
            //读取原图_imageFilePath：图片地址
            Mat srcImage = new Mat(@"D:\\Barcodepic\\0.jpg", ImreadModes.Color);
            if (srcImage.Empty()) { return; }

            //图像转换为灰度图像
            Mat grayImage = new Mat();
            OpencvHelper.CvGrayImage(srcImage, grayImage);

            //建立图像的梯度幅值
            Mat gradientImage = new Mat();
            OpencvHelper.CvConvertScaleAbs(grayImage, gradientImage);

            //对图片进行相应的模糊化,使一些噪点消除
            Mat blurImage = new Mat();
            Mat thresholdImage = new Mat();
            OpencvHelper.BlurImage(gradientImage, blurImage, thresholdImage);

            //二值化以后的图像,条形码之间的黑白没有连接起来,就要进行形态学运算,消除缝隙,相当于小型的黑洞,选择闭运算
            //因为是长条之间的缝隙,所以需要选择宽度大于长度
            Mat morphImage = new Mat();
            OpencvHelper.MorphImage(thresholdImage, morphImage);

            //现在要让条形码区域连接在一起,所以选择膨胀腐蚀,而且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀
            //先腐蚀,让其他区域的亮的地方变少最好是消除,然后膨胀回来,消除干扰,迭代次数根据实际情况选择
            OpencvHelper.DilationErosionImage(morphImage);

            Cv2.ImShow("setp1:morphImage", morphImage);
            Cv2.WaitKey(0);

            Mat[] contours = new Mat[10000];
            List<double> OutArray = new List<double>();
            //接下来对目标轮廓进行查找,目标是为了计算图像面积
            Cv2.FindContours(morphImage, out contours, OutputArray.Create(OutArray), RetrievalModes.External, ContourApproximationModes.ApproxSimple);
            //看看轮廓图像
           // Cv2.DrawContours(srcImage, contours, -1, Scalar.Red);
           // OpencvHelper.Imshow("目标轮廓", srcImage);


            //OutputArray newarry = new OutputArray();
            //计算轮廓的面积并且存放
            for (int i = 0; i < OutArray.Count; i++)
            {
                OutArray[i] = contours[i].ContourArea();
            }

            List<double> newArray = new List<double>();

            OutArray.ForEach(i => newArray.Add(i));


            for (int i = 0; i < newArray.Count - 1;i++)
            {
                if (newArray[i] < 6000 || newArray[i] > 16000)
                {
                    {
                        newArray[i] = 0;
                    }
                   
                }
            }
            for (int i = 0; i < newArray.Count - 1; i++)
            {
                if (newArray[i] != 0)
                {
                    //新建一个图像设置为指定模板图片大小，并设为黑色
                    //Mat aaa = new Mat(srcImage.Size(), MatType.CV_8UC3);
                    //aaa.SetTo(0);//全黑

                    //复制兴趣区域
                    //Rect roi = new Rect(400, 300, 400, 400);//首先要用个rect确定我们的兴趣区域在哪
                    //Mat ImageROI = new Mat(panda, roi);//新建一个mat，把roi内的图像加载到里面去


                    //这里可以对binary做一些开操作过滤噪声，这里就不演示了
                    //srcImage.CopyTo();//将原图通过mask抠图到Roi

                    Rect myRect = Cv2.BoundingRect(contours[i]);

                    Mat bbb = new Mat(srcImage, myRect);

                    //获取矩形范围并查找斜率，然后进行变换
                    //如果斜率大于一个值侧变换
                    
                    RotatedRect tmpRect = Cv2.MinAreaRect(contours[i]);
                    if (tmpRect.Angle < -2.0 )
                    {
                        //RotatedRect tmpRect = new RotatedRect( contours[i].BoundingRect, contours[i].Size);
                        rotate(bbb,0 - tmpRect.Angle, out bbb);
                        myRect.X = myRect.X - (myRect.Width / 20);
                        myRect.Width = (int)(myRect.Width * 1.1);
                        myRect.Y = myRect.Y - (myRect.Height / 10);
                        myRect.Height = (int)(myRect.Height * 1.1);
                    }
                    //if (tmpRect.Angle < -2.0)
                    //{
                    //    //RotatedRect tmpRect = new RotatedRect( contours[i].BoundingRect, contours[i].Size);
                    //    rotate(bbb, 0 - tmpRect.Angle, out bbb);
                    //    myRect.X = myRect.X - (myRect.Width / 20);
                    //    myRect.Width = (int)(myRect.Width * 1.1);
                    //    myRect.Y = myRect.Y - (myRect.Height / 10);
                    //    myRect.Height = (int)(myRect.Height * 1.1);
                    //}

                    //转换的矩形扩大范围后画框标识
                    //Cv2.Rectangle(srcImage, myRect, new Scalar(0, 255, 255), 2, LineTypes.AntiAlias);

                    //保存旋转校准
                    string path = Path.GetDirectoryName(@"D:\\Barcodepic\\out\\"+i+".jpg");
                    if (File.Exists(@"D:\\Barcodepic\\out\\" + i + ".jpg")) File.Delete(@"D:\\Barcodepic\\out\\" + i + ".jpg");//如果文件存在 则删除
                    if (!Cv2.ImWrite(@"D:\\Barcodepic\\out\\" + i + ".jpg", bbb)) ;

                }
            }
            
            
            //myRect.X = myRect.X - (myRect.Width / 20);
            //myRect.Width = (int)(myRect.Width * 1.1);
            //Mat resultImage = new Mat(srcImage myRect);
            //OpencvHelper.Imshow("结果图片", srcImage);
            




            //找出面积最大的轮廓
            //double minValue, maxValue;
            //OpenCvSharp.Point minLoc, maxLoc;
            //Cv2.MinMaxLoc(InputArray.Create(OutArray), out minValue, out maxValue, out minLoc, out maxLoc);

            //计算面积最大的轮廓的最小的外包矩形
            ////RotatedRect minRect = Cv2.MinAreaRect(contours[maxLoc.Y]);
            ////为了防止找错,要检查这个矩形的偏斜角度不能超标
            ////如果超标,那就是没找到
            //if (minRect.Angle < 2.0)
            //{
            //    //找到了矩形的角度,但是这是一个旋转矩形,所以还要重新获得一个外包最小矩形
            //    Rect myRect = Cv2.BoundingRect(contours[maxLoc.Y]);
            //    //把这个矩形在源图像中画出来
            //    //Cv2.Rectangle(srcImage, myRect, new Scalar(0, 255, 255), 3, LineTypes.AntiAlias);
            //    //看看显示效果,找的对不对
            //    //Imshow("裁剪图片", srcImage);
            //    //将扫描的图像裁剪下来,并保存为相应的结果,保留一些X方向的边界,所以对rect进行一定的扩张
            //    myRect.X = myRect.X - (myRect.Width / 20);
            //    myRect.Width = (int)(myRect.Width * 1.1);
            //    Mat resultImage = new Mat(srcImage, myRect);
            //    //OpencvHelper.Imshow("结果图片", resultImage);



            //    Image img = CreateImage(resultImage);
            //    //这个是图片控件
            //    picCode.Image = img;
            //    DiscernBarcode(img);
            //    //看看轮廓图像
            //    //Cv2.DrawContours(srcImage, contours, -1, Scalar.Red);
            //    Cv2.Rectangle(srcImage, myRect, new Scalar(0, 255, 255), 3, LineTypes.AntiAlias);
            //    Image img2 = CreateImage(srcImage);
            //    picFindContours.Image = img2;




            //    //string path = Path.GetDirectoryName(@g_sFilePath) + "\\Ok.png";
            //    //if (File.Exists(@path)) File.Delete(@path);//如果文件存在 则删除
            //    //if (!Cv2.ImWrite(@path, resultImage))
            //}
        }


        private Image CreateImage(Mat resultImage)
        {
            byte[] bytes = resultImage.ToBytes();
            MemoryStream ms = new MemoryStream(bytes);
            return Bitmap.FromStream(ms, true);
        }

        /// <summary>
        /// 解析条形码图片
        /// </summary>
        private void DiscernBarcode(Image image)
        {
            BarcodeReader reader = new BarcodeReader();
            reader.Options.CharacterSet = "UTF-8";
            Result result = reader.Decode(new Bitmap(image));//Image.FromFile(path)
            Console.Write(result);
            if (result != null)
                textBox1.Text = result.ToString();
        }

        /// <summary>
        /// 处理图片灰度
        /// </summary>
        /// <param name="original"></param>
        /// <returns></returns>
        public static Bitmap MakeGrayscale3(Bitmap original)
        {
            //create a blank bitmap the same size as original
            Bitmap newBitmap = new Bitmap(original.Width, original.Height);
            //get a graphics object from the new image
            Graphics g = Graphics.FromImage(newBitmap);
            //create the grayscale ColorMatrix
            System.Drawing.Imaging.ColorMatrix colorMatrix = new System.Drawing.Imaging.ColorMatrix(
               new float[][]
              {
                 new float[] {.3f, .3f, .3f, 0, 0},
                 new float[] {.59f, .59f, .59f, 0, 0},
                 new float[] {.11f, .11f, .11f, 0, 0},
                 new float[] {0, 0, 0, 1, 0},
                 new float[] {0, 0, 0, 0, 1}
              });
            //create some image attributes
            ImageAttributes attributes = new ImageAttributes();
            //set the color matrix attribute
            attributes.SetColorMatrix(colorMatrix);
            //draw the original image on the new image
            //using the grayscale color matrix
            g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
               0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);
            //dispose the Graphics object
            g.Dispose();
            return newBitmap;
        }


        //旋转校准
        void rotate(Mat src, float angle, out Mat dst)
        {
            dst = new Mat();
            Point2f center = new Point2f(src.Cols / 2, src.Rows / 2);
            Mat rot = Cv2.GetRotationMatrix2D(center, angle, 1);
            Size2f s2f = new Size2f(src.Size().Width, src.Size().Height);
            Rect box = new RotatedRect(new Point2f(0, 0), s2f, angle).BoundingRect();
            double xx = rot.At<double>(0, 2) + box.Width / 2 - src.Cols / 2;
            double zz = rot.At<double>(1, 2) + box.Height / 2 - src.Rows / 2;
            rot.Set(0, 2, xx);
            rot.Set(1, 2, zz);
            Cv2.WarpAffine(src, dst, rot, box.Size);
        }


        private void Button4_Click(object sender, EventArgs e)
        {
           
        }
    }
}
